////////////////////////////////////////////////////////////////////////////////
//
// (C) Andy Thomason 2011
//
// Example of drawing a triangle using OpenGL ES 2

#ifdef WIN32
  #define _CRT_SECURE_NO_WARNINGS 1
  // This is the "GL extension wrangler"
  // Note: you will need to use the right version of glew32s.lib (32 bit vs 64 bit)
  #define GLEW_STATIC
  #include "GL/glew.h"

  // This is the "GL utilities" framework for drawing with OpenGL
  #define FREEGLUT_STATIC
  #define FREEGLUT_LIB_PRAGMAS 0
  #include "GL/glut.h"
  #include "AL/al.h"
  #include "AL/alc.h"
#else // (osx?)
  #include "GLUT/glut.h"
#endif

// standard C headers
#include <list>
#include <stdio.h>
#include <math.h>
#include <assert.h>

// math support
#include "include/vector.h"
#include "include/matrix.h"

// shader wrapper
#include "include/shader.h"
#include <texture.h>

#include <file_manager.h>
#include <texture_manager.h>
#include <sound_manager.h>

class random {
  int seed;
public:
  random(unsigned new_seed = 0x9bac7615) : seed(new_seed) {
    seed = new_seed;
  }

  // get a floating point value
  float get(float min, float max) {
    seed = ( ( seed >> 31 ) & 0xa67b9c35 ) ^ ( seed << 1 );
    return min + ( ( seed >> 8 ) & 0xffff ) * ( ( max - min ) / 0xffff );
  }

  // get an int value
  int get(int min, int max) {
    assert(max - min <= 0xffff);
    seed = ( ( seed >> 31 ) & 0xa67b9c35 ) ^ ( seed << 1 );
    return min + ( ( seed >> 8 ) & 0xffff ) * ( max - min ) / 0xffff;
  }
};

// box class - holds shape and color of a box on the screen.
class box {
  vec4 center_;
  vec4 half_extents_;
  vec4 color_;
  GLuint texture_;
public:
  // we don't have a constructor, but we do have an 'init' function
  void init(float cx, float cy, float hx, float hy, GLuint texture) {
    // note that it is a convention for positions and colors
    // to have "1" in the w, distances to have "0" in the w.
    center_ = vec4(cx, cy, 0, 1);
	half_extents_ = vec4(hx, hy, 0, 0);
    color_ = vec4(0, 0, 1, 1);
	texture_ = texture;
  }

  // draw the box using a triangle fan.
  void draw(shader &shader) {
    // set the uniforms
    shader.render();

	glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture_);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


    // set the attributes    
    float vertices[4*4] = {
      center_[1] - half_extents_[1], center_[0] - half_extents_[0],0,0,
      center_[1] + half_extents_[1], center_[0] - half_extents_[0],1,0,
      center_[1] + half_extents_[1], center_[0] + half_extents_[0],1,1,
      center_[1] - half_extents_[1], center_[0] + half_extents_[0],0,1,
    };
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void*)vertices );
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void*)(vertices+2) );
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(2);
	

    // kick the draw
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  }
  
  // move the box
  void move(const vec4 &dir) {
    center_ += dir;
  }
  
   
  // the 'pos' property
  vec4 pos() const { return center_; }
  void set_pos(vec4 v) { center_ = v; }


  // return true if two boxes intersect
  bool intersects(const box &rhs) {
    vec4 diff = (rhs.pos() - pos()).abs();
    vec4 min_distance = rhs.half_extents_ + half_extents_;
    return diff[0] < min_distance[0] && diff[1] < min_distance[1];
  }
};

// the game
class GradiusX
{
  // game state: always uses enums for int constants
  enum state_t {
    state_serving,
    state_playing,
	state_end,
	};
  
  state_t state;
  random randomness;
  int hit;
  int server;
  float score;
  int counter;
  int lives;
  
  
  box fighter;
  box fire;
  box spaceship;
  vec4 fire_velocity;
  vec4 spaceship_velocity;
  int spaceship_array[20];
  int scores;
  int player;
  
  // rendering  
  shader colour_shader_;
  shader texture_shader_;
  GLint viewport_width_;
  GLint viewport_height_;
  ALuint buffers[256];

  // input
  char keys[256];
  
  // constants: always use functions for floats!
  float spaceship_speed() {	return 0.01f;}
  float fire_speed() { return 0.01f; }
  float court_size() { return 0.6f; }


  void draw_world(shader &shader) {
    fire.draw(texture_shader_);
	int i;
	for (i=counter; i <= 20; i++){
		if (spaceship_array[i]==1){
	spaceship.draw (texture_shader_);
		}}

      // draw the fighter
	
	  fighter.draw(texture_shader_);
	
	    
  }
  
  void move_fighter() {
    // look at the keys and move the bats
    vec4 fighter_right(0 , 0.02f , 0, 0);
    if (keys['d']) {
      fighter.move(fighter_right);
    }
    if (keys['a']) {
      fighter.move(-fighter_right);
    }
   }

  
 

    void do_serving() {
    // while serving, glue the fire to the fighter
    vec4 fighterpos = vec4 (0.2f,0.2f,0.2f,0.2f);
	vec4 spaceshippos = vec4 (0.7f, 0.1f,0.2f,0.2f);
    fire.set_pos(fighter.pos());
	spaceship_array[0]=1;
	spaceship.set_pos ( spaceshippos);
	spaceship_velocity = vec4 ( - spaceship_speed(), 0, 0, 0);
	if (keys[' ']) {
      state = state_playing;
	  fire_velocity = vec4( fire_speed(), 0, 0, 0);}
  }


  void do_playing() {
    // start the spaceship and the fighter
    
	vec4 new_pos = fire.pos() + fire_velocity;
	vec4 new_pos_spaceship = spaceship.pos() + spaceship_velocity;
    fire.set_pos(new_pos);
	spaceship.set_pos(new_pos_spaceship);

    if (spaceship.intersects(fire) ) {
        //spaceship_velocity = spaceship_velocity * vec4(-1, -1, 1, 1);
		counter++;
		score++;
		hit=1;
		spaceship_array[counter]=1;
		int newspaceship_modifier = randomness.get(0, 15);
		vec4 newspaceshippos= vec4 (1.0f, (newspaceship_modifier*0.1f)-0.7f,0.2f,0.2f);
		spaceship.set_pos(newspaceshippos);
		sound_hit();	
		}
	   if (spaceship.intersects(fighter) ) {
        lives--;
		state = state_serving;
		score=0;
		hit=1;
		sound_hit();
		if (lives==0)
			state = state_end;
		}
	   

	   if (new_pos_spaceship[0] < -1){  
		lives--;
		state = state_serving;
		score=0;
		hit=1;
		sound_hit();
	    if (lives==0)
		state = state_end;
	   }
	   if (new_pos[0] > -1){  
		   if (keys[' '])
				hit=1;	}
  }
	  void do_firing(){
	  if (hit==1){
	  fire.set_pos(fighter.pos());
	  fire_velocity = vec4(0, 0, 0, 0);
	  if (keys[' ']) {
      fire_velocity = vec4( fire_speed(), 0, 0, 0);
	  hit=0;
	  }}
  }

  // simulation for the game
  void simulate() {
    move_fighter();
    
    if (state == state_serving) {
      do_serving();
    } else if (state == state_playing) {
      do_playing();
	  do_firing();
	} else if (state == state_end){
		exit(0);
	}
	
  }
  	void drawString(float x, float y, float z, int t, int s)
	{ 

		glEnable(GL_COLOR_MATERIAL);
		glColor3f(0.8f, 0.2f, 0.0f);
		glRasterPos3f(x, y, z);
		char scoreArr[300];
		if (s==0)
			sprintf_s(scoreArr,"Spaceships Destroyed:" "%d", t );
		else
			sprintf_s(scoreArr,"Lives:" "%d", t );

		for (const char* c=scoreArr; *c != '\0'; c++)
		{
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c); 
		}
		

	}
  // simulate and draw the game world every frame
  void render() {
    simulate();

    // clear the frame buffer and the depth
    glClearColor(0, 0, 0, 0);
    glViewport(0, 0, viewport_width_, viewport_height_);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    
    draw_world(texture_shader_);
	drawString(-0.9f,0.9f,0, score, 0);
	drawString( 0.5f,0.9f,0, lives, 1);

    // swap buffers so that the image is displayed.
    // gets a new buffer to draw a new scene.
    glutSwapBuffers();
  }
    void sound_hit() {
    int sound = 1;
    char name[2];
    name[0] = sound + '0';
    name[1] = 0;
    sound_manager::play(vec4(0, 0, 0, 0), name);
  }
  // set up the world
  GradiusX()
  {
    memset(keys, 0, sizeof(keys));
	lives=3;
    // set up game state.
    state = state_serving;
    GLuint spaceship_texture = texture_manager::new_texture("assets/textures.tga", 0, 128, 128, 128);
	GLuint fighter_texture = texture_manager::new_texture("assets/textures.tga", 128, 128, 128, 128);
	GLuint fire_texture = texture_manager::new_texture("assets/textures.tga", 0, 0, 128, 128);
    // make the bats and ball
    float fighter_hx = 0.12f;
    float fighter_hy = 0.14f;
    float fighter_cx = 1 - fighter_hx * 2;
    fighter.init(-fighter_cx, 0, fighter_hx, fighter_hy, fighter_texture);
    float ball_hx = 0.03f;
    float ball_hy = 0.03f;
	float baloon_hx = 0.07f;
	float baloon_hy = 0.07f;
	fire.init(0, 0, ball_hx, ball_hy, fire_texture);
	spaceship.init(0, 0, baloon_hx, baloon_hy, spaceship_texture);
	sound_manager::add_buffers("assets/labels.txt", "assets/explos.wav");
    
    // set up a simple shader to render the emissve color
    texture_shader_.init(
      // just copy the position attribute to gl_Position
      "varying vec2 uv_;"
      "attribute vec4 pos;"
      "attribute vec2 uv;"
      "void main() { gl_Position = pos; uv_ = uv; }",

      // just copy the color attribute to gl_FragColor
      "varying vec2 uv_;"
      "uniform sampler2D texture;"
      "void main() { gl_FragColor = texture2D(texture, uv_); }"
    );
	

  }
  
  // The viewport defines the drawing area in the window
  void set_viewport(int w, int h) {
    viewport_width_ = w;
    viewport_height_ = h;
  }
  
  void set_key(int key, int value) {
    keys[key & 0xff] = value;
  }
public:
  // a singleton: one instance of this class only!
  static GradiusX &get()
  {
    static GradiusX singleton;
    return singleton;
  }

  // interface from GLUT
  static void reshape(int w, int h) { get().set_viewport(w, h); }
  static void display() { get().render(); }
  static void timer(int value) { glutTimerFunc(30, timer, 1); glutPostRedisplay(); }
  static void key_down( unsigned char key, int x, int y) { get().set_key(key, 1); }
  static void key_up( unsigned char key, int x, int y) { get().set_key(key, 0); }
};

// boilerplate to run the sample
int main(int argc, char **argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_DOUBLE);
  glutInitWindowSize(600, 600);
  glutCreateWindow("GradiusX");
  #ifdef WIN32
    glewInit();
    if (!glewIsSupported("GL_VERSION_2_0") )
    {
      printf("OpenGL 2 is required!\n");
      return 1;
    }
  #endif
  glutDisplayFunc(GradiusX::display);
  glutReshapeFunc(GradiusX::reshape);
  glutKeyboardFunc(GradiusX::key_down);
  glutKeyboardUpFunc(GradiusX::key_up);
  glutTimerFunc(30, GradiusX::timer, 1);
  glutMainLoop();
  return 0;
}

